;; Test `block` operator

(module
  (import "wasi" "print_i32_ln" (func $wasi$print_i32_ln (param i32)))

  ;; Auxiliary definition
  (memory 1)

  (func $dummy)

  (func $empty (export "empty")
    (block)
    (block $l)
  )

  (func $singular (export "singular") (result i32)
    (block (nop))
    (block (result i32) (i32.const 7))
  )

  (func $multi (export "multi") (result i32)
    (block (call $dummy) (call $dummy) (call $dummy) (call $dummy))
    (block (result i32)
      (call $dummy) (call $dummy) (call $dummy) (i32.const 7) (call $dummy)
    )
    (drop)
    (block (result i32 i64 i32)
      (call $dummy) (call $dummy) (call $dummy) (i32.const 8) (call $dummy)
      (call $dummy) (call $dummy) (call $dummy) (i64.const 7) (call $dummy)
      (call $dummy) (call $dummy) (call $dummy) (i32.const 9) (call $dummy)
    )
    (drop) (drop)
  )

  (func $nested (export "nested") (result i32)
    (block (result i32)
      (block (call $dummy) (block) (nop))
      (block (result i32) (call $dummy) (i32.const 9))
    )
  )

  (func $deep (export "deep") (result i32)
    (block (result i32) (block (result i32)
      (block (result i32) (block (result i32)
        (block (result i32) (block (result i32)
          (block (result i32) (block (result i32)
            (block (result i32) (block (result i32)
              (block (result i32) (block (result i32)
                (block (result i32) (block (result i32)
                  (block (result i32) (block (result i32)
                    (block (result i32) (block (result i32)
                      (block (result i32) (block (result i32)
                        (block (result i32) (block (result i32)
                          (block (result i32) (block (result i32)
                            (block (result i32) (block (result i32)
                              (block (result i32) (block (result i32)
                                (block (result i32) (block (result i32)
                                  (block (result i32) (block (result i32)
                                    (block (result i32) (block (result i32)
                                      (block (result i32) (block (result i32)
                                        (block (result i32) (block (result i32)
                                          (call $dummy) (i32.const 150)
                                        ))
                                      ))
                                    ))
                                  ))
                                ))
                              ))
                            ))
                          ))
                        ))
                      ))
                    ))
                  ))
                ))
              ))
            ))
          ))
        ))
      ))
    ))
  )

  (func $as_select_first (export "as_select_first") (result i32)
    (select (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 3))
  )
  (func $as_select_mid (export "as_select_mid") (result i32)
    (select (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 3))
  )
  (func $as_select_last (export "as_select_last") (result i32)
    (select (i32.const 2) (i32.const 3) (block (result i32) (i32.const 1)))
  )

  (func $as_loop_first (export "as_loop_first") (result i32)
    (loop (result i32) (block (result i32) (i32.const 1)) (call $dummy) (call $dummy))
  )
  (func $as_loop_mid (export "as_loop_mid") (result i32)
    (loop (result i32) (call $dummy) (block (result i32) (i32.const 1)) (call $dummy))
  )
  (func $as_loop_last (export "as_loop_last") (result i32)
    (loop (result i32) (call $dummy) (call $dummy) (block (result i32) (i32.const 1)))
  )

  (func $as_if_condition (export "as_if_condition")
    (block (result i32) (i32.const 1)) (if (then (call $dummy)))
  )
  (func $as_if_then (export "as_if_then") (result i32)
    (if (result i32) (i32.const 1) (then (block (result i32) (i32.const 1))) (else (i32.const 2)))
  )
  (func $as_if_else (export "as_if_else") (result i32)
    (if (result i32) (i32.const 1) (then (i32.const 2)) (else (block (result i32) (i32.const 1))))
  )

  (func $as_br_if_first (export "as_br_if_first") (result i32)
    (block (result i32) (br_if 0 (block (result i32) (i32.const 1)) (i32.const 2)))
  )
  (func $as_br_if_last (export "as_br_if_last") (result i32)
    (block (result i32) (br_if 0 (i32.const 2) (block (result i32) (i32.const 1))))
  )

  (func $as_br_table_first (export "as_br_table_first") (result i32)
    (block (result i32) (block (result i32) (i32.const 1)) (i32.const 2) (br_table 0 0))
  )
  (func $as_br_table_last (export "as_br_table_last") (result i32)
    (block (result i32) (i32.const 2) (block (result i32) (i32.const 1)) (br_table 0 0))
  )

  (func $func (param i32 i32) (result i32) (local.get 0))
  (type $check (func (param i32 i32) (result i32)))
 ;; (table funcref (elem $func))
 ;; (func $as_call_indirect_first (export "as-call_indirect-first") (result i32)
 ;;   (block (result i32)
 ;;     (call_indirect (type $check)
 ;;       (block (result i32) (i32.const 1)) (i32.const 2) (i32.const 0)
 ;;     )
 ;;   )
 ;; )
 ;; (func $as_call_indirect_mid (export "as-call_indirect-mid") (result i32)
 ;;   (block (result i32)
 ;;     (call_indirect (type $check)
 ;;       (i32.const 2) (block (result i32) (i32.const 1)) (i32.const 0)
 ;;     )
 ;;   )
 ;; )
 ;; (func $as_call_indirect_last (export "as-call_indirect-last") (result i32)
 ;;   (block (result i32)
 ;;     (call_indirect (type $check)
 ;;       (i32.const 1) (i32.const 2) (block (result i32) (i32.const 0))
 ;;     )
 ;;   )
 ;; )

  (func $as_store_first (export "as_store_first")
    (block (result i32) (i32.const 1)) (i32.const 1) (i32.store)
  )
  (func $as_store_last (export "as_store_last")
    (i32.const 10) (block (result i32) (i32.const 1)) (i32.store)
  )

 ;; (func $as_memory_grow_value (export "as_memory_grow_value") (result i32)
 ;;   (memory.grow (block (result i32) (i32.const 1)))
 ;; )

  (func $f (param i32) (result i32) (local.get 0))

  (func $as_call_value (export "as_call_value") (result i32)
    (call $f (block (result i32) (i32.const 1)))
  )
  (func $as_return_value (export "as_return_value") (result i32)
    (block (result i32) (i32.const 1)) (return)
  )
  (func $as_drop_operand (export "as_drop_operand")
    (drop (block (result i32) (i32.const 1)))
  )
  (func $as_br_value (export "as_br_value") (result i32)
    (block (result i32) (br 0 (block (result i32) (i32.const 1))))
  )
  (func $as_local_set_value (export "as_local_set_value") (result i32)
    (local i32) (local.set 0 (block (result i32) (i32.const 1))) (local.get 0)
  )
  (func $as_local_tee_value (export "as_local_tee_value") (result i32)
    (local i32) (local.tee 0 (block (result i32) (i32.const 1)))
  )
  (global $a (mut i32) (i32.const 10))
  (func $as_global_set_value (export "as_global_set_value") (result i32)
    (global.set $a (block (result i32) (i32.const 1)))
    (global.get $a)
  )

  (func $as_load_operand (export "as_load_operand") (result i32)
    (i32.load (block (result i32) (i32.const 1)))
  )

  (func $as_unary_operand (export "as_unary_operand") (result i32)
    (i32.ctz (block (result i32) (call $dummy) (i32.const 13)))
  )
  (func $as_binary_operand (export "as_binary_operand") (result i32)
    (i32.mul
      (block (result i32) (call $dummy) (i32.const 3))
      (block (result i32) (call $dummy) (i32.const 4))
    )
  )
  (func $as_test_operand (export "as_test_operand") (result i32)
    (i32.eqz (block (result i32) (call $dummy) (i32.const 13)))
  )
  (func $as_compare_operand (export "as_compare_operand") (result i32)
    (f32.gt
      (block (result f32) (call $dummy) (f32.const 3))
      (block (result f32) (call $dummy) (f32.const 3))
    )
  )
 (func $as_binary_operands (export "as_binary_operands") (result i32)
   (i32.mul
     (block (result i32 i32)
       (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)
     )
   )
 )
 (func $as_compare_operands (export "as_compare_operands") (result i32)
   (f32.gt
     (block (result f32 f32)
       (call $dummy) (f32.const 3) (call $dummy) (f32.const 3)
     )
   )
 )
 (func $as_mixed_operands (export "as_mixed_operands") (result i32)
   (block (result i32 i32)
     (call $dummy) (i32.const 3) (call $dummy) (i32.const 4)
   )
   (i32.const 5)
   (i32.add)
   (i32.mul)
 )

  (func $break_bare (export "break_bare") (result i32)
    (block (br 0) (unreachable))
    (block (br_if 0 (i32.const 1)) (unreachable))
    (block (br_table 0 (i32.const 0)) (unreachable))
    (block (br_table 0 0 0 (i32.const 1)) (unreachable))
    (i32.const 19)
  )
  ;;(func $break_value (export "break_value") (result i32)
  ;;  (block (result i32) (br 0 (i32.const 18)) (i32.const 19))
  ;;)

  ;;(func $break_multi_value (export "break_multi_value") (result i32 i32 i64)
  ;;  (block (result i32 i32 i64)
  ;;    (br 0 (i32.const 18) (i32.const -18) (i64.const 18))
  ;;    (i32.const 19) (i32.const -19) (i64.const 19)
  ;;  )
  ;;)

 ;; (func $break_repeated (export "break_repeated") (result i32)
 ;;   (block (result i32)
 ;;     (br 0 (i32.const 18))
 ;;     (br 0 (i32.const 19))
 ;;     (drop (br_if 0 (i32.const 20) (i32.const 0)))
 ;;     (drop (br_if 0 (i32.const 20) (i32.const 1)))
 ;;     (br 0 (i32.const 21))
 ;;     (br_table 0 (i32.const 22) (i32.const 4))
 ;;     (br_table 0 0 0 (i32.const 23) (i32.const 1))
 ;;     (i32.const 21)
 ;;   )
 ;; )

 (func $break_inner (export "break_inner") (result i32)
   (local i32)
   (local.set 0 (i32.const 0))
   (local.set 0 (i32.add (local.get 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1))))))
   (local.set 0 (i32.add (local.get 0) (block (result i32) (block (br 0)) (i32.const 0x2))))
   (local.set 0
     (i32.add (local.get 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4)))))
   )
   (local.set 0
     (i32.add (local.get 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8))))))
   )
   (local.get 0)
 )

  (func $param (export "param") (result i32)
    (i32.const 1)
    (block (param i32) (result i32)
      (i32.const 2)
      (i32.add)
    )
  )
  (func $params (export "params") (result i32)
    (i32.const 1)
    (i32.const 2)
    (block (param i32 i32) (result i32)
      (i32.add)
    )
  )
  (func $params_id (export "params_id") (result i32)
    (i32.const 1)
    (i32.const 2)
    (block (param i32 i32) (result i32 i32))
    (i32.add)
  )
  (func $param_break (export "param_break") (result i32)
    (i32.const 1)
    (block (param i32) (result i32)
      (i32.const 2)
      (i32.add)
      (br 0)
    )
  )
  (func $params_break (export "params_break") (result i32)
    (i32.const 1)
    (i32.const 2)
    (block (param i32 i32) (result i32)
      (i32.add)
      (br 0)
    )
  )
  (func $params_id_break (export "params_id_break") (result i32)
    (i32.const 1)
    (i32.const 2)
    (block (param i32 i32) (result i32 i32) (br 0))
    (i32.add)
  )

  (func $effects (export "effects") (result i32)
    (local i32)
    (block
      (local.set 0 (i32.const 1))
      (local.set 0 (i32.mul (local.get 0) (i32.const 3)))
      (local.set 0 (i32.sub (local.get 0) (i32.const 5)))
      (local.set 0 (i32.mul (local.get 0) (i32.const 7)))
      (br 0)
      (local.set 0 (i32.mul (local.get 0) (i32.const 100)))
    )
    (i32.eq (local.get 0) (i32.const -14))
  )

  (type $block-sig-1 (func))
  (type $block-sig-2 (func (result i32)))
  (type $block-sig-3 (func (param $x i32)))
  (type $block-sig-4 (func (param i32 f64 i32) (result i32 f64 i32)))

  (func $type_use (export "type_use")
    (block (type $block-sig-1))
    (block (type $block-sig-2) (i32.const 0))
    (block (type $block-sig-3) (drop))
    (i32.const 0) (f64.const 0) (i32.const 0)
    (block (type $block-sig-4))
    (drop) (drop) (drop)
    (block (type $block-sig-2) (result i32) (i32.const 0))
    (block (type $block-sig-3) (param i32) (drop))
    (i32.const 0) (f64.const 0) (i32.const 0)
    (block (type $block-sig-4)
      (param i32) (param f64 i32) (result i32 f64) (result i32)
    )
    (drop) (drop) (drop)
  )

(func $assert_eq_i32 (param $line i32) (param $x i32) (param $y i32)
       (if (i32.ne (local.get 1) (local.get 2))
           (then
	       (call $wasi$print_i32_ln  (local.get 0))
	       (call $wasi$print_i32_ln  (local.get 1))
	       (call $wasi$print_i32_ln  (local.get 2))
	       (unreachable))
       )
 )
 
 (func $main (export "_start")
  (call $empty )
  (call $assert_eq_i32 (i32.const 1201) (call $singular ) (i32.const 7))
  (call $assert_eq_i32 (i32.const 1301) (call $multi ) (i32.const 8))
  (call $assert_eq_i32 (i32.const 1401) (call $nested ) (i32.const 9))
  (call $assert_eq_i32 (i32.const 1501) (call $deep ) (i32.const 150))

  (call $assert_eq_i32 (i32.const 1601) (call $as_select_first ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 1701) (call $as_select_mid ) (i32.const 2))
  (call $assert_eq_i32 (i32.const 1801) (call $as_select_last ) (i32.const 2))

  (call $assert_eq_i32 (i32.const 1901) (call $as_loop_first ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 2001) (call $as_loop_mid ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 2101) (call $as_loop_last ) (i32.const 1))

  (call $as_if_condition )
  (call $assert_eq_i32 (i32.const 2301) (call $as_if_then ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 2401) (call $as_if_else ) (i32.const 2))

  (call $assert_eq_i32 (i32.const 2501) (call $as_br_if_first ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 2601) (call $as_br_if_last ) (i32.const 2))

  (call $assert_eq_i32 (i32.const 2701) (call $as_br_table_first ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 2801) (call $as_br_table_last ) (i32.const 2))

  ;;(call $assert_eq_i32 (i32.const 2901) (call $as_call_indirect_first ) (i32.const 1))
  ;;(call $assert_eq_i32 (i32.const 3001) (call $as_call_indirect_mid ) (i32.const 2))
  ;;(call $assert_eq_i32 (i32.const 3101) (call $as_call_indirect_last ) (i32.const 1))

  (call $as_store_first )
  (call $as_store_last )

  ;;(call $assert_eq_i32 (i32.const 3401) (call $as_memory_grow_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 3501) (call $as_call_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 3601) (call $as_return_value ) (i32.const 1))
  (call $as_drop_operand )
  (call $assert_eq_i32 (i32.const 3801) (call $as_br_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 3901) (call $as_local_set_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 4001) (call $as_local_tee_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 4101) (call $as_global_set_value ) (i32.const 1))
  (call $assert_eq_i32 (i32.const 4201) (call $as_load_operand ) (i32.const 1))

  (call $assert_eq_i32 (i32.const 4301) (call $as_unary_operand ) (i32.const 0))
  (call $assert_eq_i32 (i32.const 4401) (call $as_binary_operand ) (i32.const 12))
  (call $assert_eq_i32 (i32.const 4501) (call $as_test_operand ) (i32.const 0))
  (call $assert_eq_i32 (i32.const 4601) (call $as_compare_operand ) (i32.const 0))
  (call $assert_eq_i32 (i32.const 4701) (call $as_binary_operands ) (i32.const 12))
  (call $assert_eq_i32 (i32.const 4801) (call $as_compare_operands ) (i32.const 0))
  (call $assert_eq_i32 (i32.const 4901) (call $as_mixed_operands ) (i32.const 27))

  (call $assert_eq_i32 (i32.const 5001) (call $break_bare ) (i32.const 19))
  ;;(call $assert_eq_i32 (i32.const 5101) (call $break_value ) (i32.const 18))

  ;;  (call $assert_eq_i32_i32_i64 (call $break_multi_value) (i32.const 18) (i32.const -18) (i64.const 18))

  ;; (call $assert_eq_i32 (i32.const 5301) (call $break_repeated ) (i32.const 18))
  (call $assert_eq_i32 (i32.const 5401) (call $break_inner ) (i32.const 0xf))

  (call $assert_eq_i32 (i32.const 5501) (call $param ) (i32.const 3))
  (call $assert_eq_i32 (i32.const 5601) (call $params ) (i32.const 3))
  (call $assert_eq_i32 (i32.const 5701) (call $params_id ) (i32.const 3))
  (call $assert_eq_i32 (i32.const 5801) (call $param_break ) (i32.const 3))
  (call $assert_eq_i32 (i32.const 5901) (call $params_break ) (i32.const 3))
  (call $assert_eq_i32 (i32.const 6001) (call $params_id_break ) (i32.const 3))

  (call $assert_eq_i32 (i32.const 6101) (call $effects ) (i32.const 1))

  (call $type_use)
  (call $wasi$print_i32_ln  (i32.const 666))
)
)
